<template>
  <!-- 索引列表 -->
  <view class="root" :style="{ height: height || contentHeight }">
    <!-- scroll-view 开启动画如果切换元素过快会出现不准确  -->
    <scroll-view
      :scroll-top="scrollTop"
      class="scrollView"
      :scroll-into-view="currentScrollIntoView"
      @scroll="scroll"
      scroll-y="true"
    >
      <!-- <slot name="topContent" /> -->
      <slot
        :indexList="indexList"
        v-if="list.length > 0 || customList.length > 0"
      />
      <!-- <slot name="bottomContent" /> -->
    </scroll-view>

    <view
      class="indexBox"
      @touchend="touchend"
      @touchstart="touchstart"
      @touchmove="touchmove"
    >
      <view
        class="index"
        :style="
          currentIndex == index
            ? `background:${indexActiveColor};color:white`
            : ''
        "
        v-for="(item, index) in indexList"
        :key="index"
      >
        {{ item.letter }}
      </view>
    </view>

    <view
      class="moveBox"
      :class="isTouch ? 'show' : ''"
      :style="[moveBoxStyles]"
    >
      <view class="text" v-if="currentIndex != null">
        {{ indexList[currentIndex].letter }}
      </view>
    </view>
  </view>
</template>

<script>
import { convertPinyin, debounce } from './utils/index.js';

import props from './props';

export default {
  name: 'indexList',
  props,
  data() {
    return {
      children: [],
      contentHeight: `${uni.getSystemInfoSync().windowHeight}px`,
      currentIndex: null, // 当前索引
      currentScrollIntoView: '', // 位移元素(由于跳转元素不在一个页面中，再加上uni不能动态slot) 故用scrollTop替代
      scrollTop: 0,
      indexList: [], // 数据
      indexBoxInfo: {
        height: 0, // 高度
        childHeight: 0, // 单个子节点高度,
        top: 0, // 距离顶部的高度 ， 手指移入实践是从最顶部开始计算的
        moveBoxHeight: 0, // 移动盒子的高度
      },
      isTouch: false, // 是否为手指滑动期间，滑动期间不处理滚动事件
    };
  },

  methods: {
    getDomInfo(selector, all) {
      // 可一次查多个元素selectAll
      return new Promise((resolve) => {
        uni
          .createSelectorQuery()
          .in(this)
          [all ? 'selectAll' : 'select'](selector)
          .boundingClientRect((rect) => {
            if (all && Array.isArray(rect) && rect.length) {
              resolve(rect);
            }
            if (!all && rect) {
              resolve(rect);
            }
          })
          .exec();
      });
    },

    touchstart(e) {
      this.isTouch = true;
      // 只处理单个手指触摸
      this.touchElements(e.changedTouches[0].pageY);
    },

    touchElements(e) {
      const index = Math.floor(
        (e - this.indexBoxInfo.top) / this.indexBoxInfo.childHeight
      );
      this.goIndexView(index);
    },

    touchmove(e) {
      this.isTouch = true;
      this.touchElements(e.changedTouches[0].pageY);
    },

    touchend() {
      // 防止动画消失太快
      setTimeout(() => {
        this.isTouch = false;
      }, 300);
    },

    getChildComponentsList() {
      // vue3移除了$children，在子组件中通过 $parent 传过来
      return this.children;

      // #ifdef H5
      return this.$children[0].$children[0].$children;
      // #endif

      // #ifdef APP-PLUS
      return this.$children;
      // #endif

      //  #ifndef  VUE3
      return this.$children[0].$children;
      // #endif

      // #ifdef VUE3
      return this.$children;
      // #endif

      //
    },

    async goIndexView(index) {
      if (index == this.currentIndex) return;
      index =
        index > this.indexList.length - 1 ? this.indexList.length - 1 : index;
      index = index < 0 ? 0 : index;
      this.currentIndex = index;
      let height = 0;
      this.getChildComponentsList().map((item, i) => {
        if (i <= index - 1) {
          height += item.height;
        }
      });
      this.scrollTop = height;
      // this.currentScrollIntoView = `lineBox${index}`
    },

    scroll: debounce(async function (e) {
      if (this.isTouch) return;
      let height = 0;
      let indexs = 0;
      this.getChildComponentsList().map((item, index) => {
        height += item.height;
        if (e.detail.scrollTop >= height) {
          indexs = index + 1;
        }
      });
      this.currentIndex = indexs;
    }, 0),

    async initData() {
      if (this.customList.length > 0) {
        this.indexList = this.customList;
      } else {
        const generateArray = () => {
          const array = JSON.parse(JSON.stringify(this.list));
          array.map((item, index) => {
            item.name = item[this.retrieveProperties];
            item.pinyin = convertPinyin(item[this.retrieveProperties], 1);
          });
          const arrGenerate = [];
          for (let i = 0; i < 26; i++) {
            var letter = String.fromCharCode(65 + i);
            const obj = {
              name: letter,
              data: [],
            };
            array.map((item, index) => {
              if (item.pinyin == letter || item.pinyin == letter.toLowerCase())
                obj.data.push(item);
            });
            if (this.showEmptyDataElement || obj.data.length > 0) {
              arrGenerate.push(obj);
            }
          }
          return arrGenerate;
        };
        this.indexList = generateArray();
      }

      this.$nextTick(() => {
        setTimeout(async () => {
          const indexBoxDomInfo = await this.getDomInfo('.indexBox');
          const moveBoxInfo = await this.getDomInfo('.moveBox');
          indexBoxDomInfo.moveBoxHeight = moveBoxInfo.height;
          indexBoxDomInfo.childHeight =
            indexBoxDomInfo.height / this.indexList.length;
          this.indexBoxInfo = indexBoxDomInfo;
        }, 50);
      });
    },
  },

  async mounted() {
    // this.initData()
  },

  created() {
    // if(this.height) return
    // let height = uni.getSystemInfoSync().windowHeight
    // // #ifdef H5
    // if(!this.h5NoTopBar){
    // 	height -= 44
    // }
    // // #endif
    // this.contentHeight = height + 'px'
  },

  computed: {
    moveBoxStyles() {
      const { childHeight, top, moveBoxHeight } = this.indexBoxInfo;

      let calcTop =
        childHeight * (this.currentIndex + 1) - moveBoxHeight / 2 + top;

      // #ifdef H5
      if (!this.h5NoTopBar) {
        calcTop += 44;
      }
      // #endif
      const style = {
        top: `${calcTop}px`,
        ...this.moveBoxStyle,
      };
      return style;
    },
  },
  options: {
    styleIsolation: 'shared',
    virtualHost: true,
  },

  watch: {
    list() {
      if (this.indexList.length <= 0) {
        this.initData();
      }
    },
    customList: {
      handler() {
        this.currentIndex = null;
        this.initData();
      },
      deep: true,
    },
  },
};
</script>

<style lang="scss" scoped>
.root {
  position: relative;

  .scrollView {
    height: 100%;
    // background: seagreen;
    overflow: auto;
    display: flex;
    flex-direction: column;
    position: relative;
  }

  .indexBox {
    z-index: 56;
    top: 50%;
    transform: translate(0, -50%);
    display: flex;
    position: absolute;
    flex-direction: column;
    right: 0;
    gap: 5rpx;

    .index {
      width: 35rpx;
      height: 35rpx;
      color: white;
      font-size: 22rpx;
      color: #606266;
      display: flex;
      align-items: center;
      border-radius: 50%;
      justify-content: center;
    }

    // .active {
    // 	background: #4293f7;
    // 	color: white;

    // }
  }

  .moveBox {
    z-index: 99;
    width: 100rpx;
    height: 100rpx;
    background: white;
    position: fixed;
    right: 60rpx;
    top: 0;
    transform: rotate(-45deg);
    border-radius: 100px 100px 0 100px;
    transition: all 0.2s;
    opacity: 0;
    display: flex;
    align-items: center;
    justify-content: center;

    .text {
      transform: rotate(45deg);
      // color: white;
      font-size: 30rpx;
    }
  }

  .noShow {
  }

  .show {
    opacity: 1;
  }
}
</style>
